package com.cavy.platform.client.service;

import com.cavy.platform.client.CavyConfig;
import com.cavy.platform.client.domain.ContainerInfo;
import com.cavy.platform.client.domain.*;
import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.exceptions.DockerCertificateException;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.messages.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

@Service
@Slf4j
public class SpotifyDockerServiceImpl implements DockerService {
    private DockerClient client;
    private SpotifyDockerEventServiceImpl eventService;
    @Autowired
    private CavyConfig config;

    @PostConstruct
    public void init() throws DockerCertificateException {
        client = DefaultDockerClient.fromEnv().build();
        eventService = new SpotifyDockerEventServiceImpl(client);
    }

    @PreDestroy
    public void destroy() {
        client.close();
        eventService.destroy();
    }

    /**
     * 1,检查是否有该镜像
     * 2.1,如果不存在镜像,则先pull
     * 2.2,生成容器运行参数
     * 3,运行并设置dockerContainerId
     *
     * @param containerInfo domain信息
     */
    @Override
    public void createContainer(ContainerInfo containerInfo) throws ContainerRunningException {
        String image = containerInfo.getImage();
        image = formatImage(image);
        try {
            boolean exist = checkImageExist(image);
            if (!exist) {
                pullImage(image);
            }
            ContainerConfig containerConfig = buildContainerConfig(containerInfo);
            ContainerCreation container = client.createContainer(containerConfig, getContainerName());
            containerInfo.setDockerContainerId(container.id());
        } catch (DockerException | InterruptedException e) {
            log.error("创建容器发生错误,错误类型:{},错误信息:", e.getClass(), e.getMessage());
            throw new ContainerRunningException(e);
        }
    }

    private String getContainerName() {
        return CONTAINER_PRE + UUID.randomUUID().toString();
    }

    private ContainerConfig buildContainerConfig(ContainerInfo containerInfo) {
        ContainerConfig.Builder builder = ContainerConfig.builder()
                .image(containerInfo.getImage());
        if (!StringUtils.isEmpty(containerInfo.getCommand())) {
            builder.cmd(containerInfo.getCommand().split(SPLIT));
        }
        if (!StringUtils.isEmpty(containerInfo.getUser())) {
            builder.user(containerInfo.getUser());
        }
        HostConfig.Builder hostConfig = HostConfig.builder();
        //挂载卷
        List<ContainerVolume> volumes = containerInfo.getVolumes();
        HostConfig.Bind[] binds = new HostConfig.Bind[volumes.size()];
        int i = 0;
        for (ContainerVolume volume : volumes) {
            HostConfig.Bind build = HostConfig.Bind.builder().to(volume.getContainerPath()).from(volume.getHostPath()).readOnly(false).build();
            binds[i] = build;
            i++;
        }
        hostConfig.appendBinds(binds);

        //环境变量
        builder.env(containerInfo.toEnv());
        if (!StringUtils.isEmpty(containerInfo.getWorkDir())) {
            builder.workingDir(containerInfo.getWorkDir());
        }
        //link
        List<StageDependency> dependencyList = containerInfo.getDependencyList();
        List<String> links = new ArrayList<>(dependencyList.size());
        for (StageDependency dependency : dependencyList) {
            String linkName;
            if (StringUtils.isEmpty(dependency.getLinkName())) {
                linkName = dependency.getDockerContainerId();
            } else {
                linkName = dependency.getLinkName();
            }
            links.add(dependency.getDockerContainerId() + ":" + linkName);
        }
        hostConfig.links(links);
        //日志
        HashMap<String, String> logOptions = new HashMap<>();
        logOptions.put(TAG, containerInfo.getContainerTagName());
        logOptions.put(FLUENTD_ADDRESS, config.getLog().getUri());
        logOptions.put(FLUENTD_ASYNC_CONNECT, Boolean.toString(true));
        LogConfig fluentd = LogConfig.create(LOG_DRIVER, logOptions);
        hostConfig.logConfig(fluentd);
        builder.hostConfig(hostConfig.build());
        return builder.build();
    }

    private void pullImage(String image) throws DockerException, InterruptedException {
        client.pull(image);
    }

    private boolean checkImageExist(String image) throws DockerException, InterruptedException {
        DockerClient.ListImagesParam listImagesParam = DockerClient.ListImagesParam.byName(image);
        List<Image> images = client.listImages(listImagesParam);
        return !images.isEmpty();
    }

    private String formatImage(String image) {
        if (!image.contains(":")) {
            image = image + ":latest";
            return image;
        }
        return image;
    }

    @Override
    public void registerEventHandler(DockerEventHandler handler) {
        eventService.registerEventHandler(handler);
    }

    @Override
    public void startContainer(String dockerContainerId) throws ContainerRunningException {
        try {
            client.startContainer(dockerContainerId);
        } catch (DockerException | InterruptedException e) {
            log.error("启动容器发生错误,错误类型:{},错误信息:", e.getClass(), e.getMessage());
            throw new ContainerRunningException(e);
        }
    }

    @Override
    public void removeContainer(ContainerInfo containerInfo) throws ContainerRunningException {
        //查询container是否存在,如果不存在则不做任何操作
        String dockerContainerId = containerInfo.getDockerContainerId();
        try {
            com.spotify.docker.client.messages.ContainerInfo info = client.inspectContainer(dockerContainerId);
            Boolean running = info.state().running();
            if (running) {
                stopContainer(dockerContainerId);
            }
            client.removeContainer(dockerContainerId);
        } catch (DockerException | InterruptedException e) {
            log.error("启动容器发生错误,错误类型:{},错误信息:", e.getClass(), e.getMessage());
            throw new ContainerRunningException(e);
        }
    }

    private void stopContainer(String dockerContainerId) throws DockerException, InterruptedException {
        client.killContainer(dockerContainerId);
    }
}
